package com.clouddms.service.storage;

import com.clouddms.entity.Backup;
import com.clouddms.entity.FileInfo;
import com.clouddms.repository.BackupRepository;
import com.clouddms.repository.FileRepository;
import com.clouddms.service.common.IService;
import com.clouddms.service.common.ServiceStatus;
import com.clouddms.service.security.FileIntegrityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.*;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/**
 * 备份服务
 * 负责文件的自动备份、手动备份和备份管理
 */
@Service
public class BackupService implements IService {

    private static final Logger logger = LoggerFactory.getLogger(BackupService.class);

    @Autowired
    private BackupRepository backupRepository;

    @Autowired
    private FileRepository fileRepository;

    @Autowired
    private FileIntegrityService fileIntegrityService;

    @Value("${clouddms.backup.enabled:true}")
    private boolean backupEnabled;

    @Value("${clouddms.backup.autoBackupInterval}")
    private int autoBackupInterval;

    @Value("${clouddms.backup.backupBasePath}")
    private String backupBasePath;

    @Value("${clouddms.backup.autoBackupEnabled:true}")
    private boolean autoBackupEnabled;

    @Value("${clouddms.backup.maxBackupSize:5}")
    private long maxBackupSize;


    @Value("${clouddms.backup.retention-days:30}")
    private int retentionDays;

    @Value("${clouddms.backup.max-file-size:1073741824}") // 1GB
    private long maxBackupFileSize;

    /**
     * 创建文件备份
     * @param fileId 文件ID
     * @param backupType 备份类型
     * @return 备份结果
     */
    @Transactional
    public BackupResult createBackup(Long fileId, String backupType) {
        if (!backupEnabled) {
            logger.warn("备份服务已禁用，跳过备份文件: {}", fileId);
            return BackupResult.failure("备份服务已禁用");
        }

        try {
            logger.info("开始备份文件: fileId={}, type={}", fileId, backupType);

            // 获取文件信息
            Optional<FileInfo> fileOpt = fileRepository.findById(fileId);
            if (!fileOpt.isPresent()) {
                logger.warn("备份失败 - 文件不存在: {}", fileId);
                return BackupResult.failure("文件不存在");
            }

            FileInfo fileInfo = fileOpt.get();

            // 检查文件是否可用
            if (!fileInfo.isAvailable()) {
                logger.warn("备份失败 - 文件不可用: {}", fileId);
                return BackupResult.failure("文件不可用");
            }

            // 检查文件大小限制
            if (fileInfo.getFileSize() > maxBackupFileSize) {
                logger.warn("备份失败 - 文件过大: fileId={}, size={}", fileId, fileInfo.getFileSize());
                return BackupResult.failure("文件大小超过备份限制");
            }

            // 检查是否已存在备份
            Optional<Backup> existingBackup = backupRepository.findLatestBackupByFileId(fileId);
            if (existingBackup.isPresent() && Backup.BackupType.AUTO.equals(backupType)) {
                // 自动备份时检查时间间隔，避免频繁备份
                LocalDateTime lastBackupTime = existingBackup.get().getCreatedAt();
                if (lastBackupTime.isAfter(LocalDateTime.now().minusHours(1))) {
                    logger.debug("跳过自动备份 - 距离上次备份时间过短: {}", fileId);
                    return BackupResult.success(existingBackup.get(), "使用现有备份");
                }
            }

            // 生成备份路径
            Path originalPath = Paths.get(fileInfo.getFilePath());
            Path backupPath = generateBackupPath(fileInfo);

            // 确保备份目录存在
            createDirectoriesIfNotExists(backupPath.getParent());

            // 复制文件到备份位置
            Files.copy(originalPath, backupPath, StandardCopyOption.REPLACE_EXISTING);

            // 验证备份文件完整性
            String originalHash = fileInfo.getMd5Hash();
            FileIntegrityService.FileIntegrityInfo result = fileIntegrityService.calculateFileIntegrity(backupPath.toString(), FileIntegrityService.HashAlgorithm.MD5);
            if(!result.isSuccess())
            {
                logger.warn("创建备份文件失败 - 输入文件已损坏: fileId={}", fileId);
                return BackupResult.failure("输入文件完整性验证失败");
            }

            String backupHash = result.getHash(FileIntegrityService.HashAlgorithm.MD5);
            if (!originalHash.equals(backupHash)) {
                // 备份文件哈希不匹配，删除备份文件
                Files.deleteIfExists(backupPath);
                logger.error("备份失败 - 文件完整性验证失败: fileId={}", fileId);
                return BackupResult.failure("备份文件完整性验证失败");
            }

            // 创建备份记录
            Backup backup = new Backup();
            backup.setFileId(fileId);
            backup.setBackupPath(backupPath.toString());
            backup.setBackupType(backupType);
            backup.setBackupSize(Files.size(backupPath));
            backup.setBackupFileHash(backupHash);

            backup = backupRepository.save(backup);

            logger.info("文件备份成功: fileId={}, backupId={}, path={}", 
                    fileId, backup.getBackupId(), backupPath);

            return BackupResult.success(backup, "备份创建成功");

        } catch (Exception e) {
            logger.error("创建备份时发生错误: fileId={}", fileId, e);
            return BackupResult.failure("创建备份失败: " + e.getMessage());
        }
    }

    /**
     * 异步创建备份
     * @param fileId 文件ID
     * @param backupType 备份类型
     * @return 异步备份结果
     */
    @Async
    public CompletableFuture<BackupResult> createBackupAsync(Long fileId, String backupType) {
        BackupResult result = createBackup(fileId, backupType);
        return CompletableFuture.completedFuture(result);
    }

    /**
     * 手动备份文件
     * @param fileId 文件ID
     * @return 备份结果
     */
    public BackupResult manualBackup(Long fileId) {
        return createBackup(fileId, Backup.BackupType.MANUAL);
    }

    /**
     * 自动备份文件
     * @param fileId 文件ID
     * @return 备份结果
     */
    public BackupResult autoBackup(Long fileId) {
        return createBackup(fileId, Backup.BackupType.AUTO);
    }

    /**
     * 恢复文件从备份
     * @param backupId 备份ID
     * @return 恢复结果
     */
    @Transactional
    public boolean restoreFromBackup(Long backupId) {
        try {
            logger.info("开始从备份恢复文件: backupId={}", backupId);

            Optional<Backup> backupOpt = backupRepository.findById(backupId);
            if (!backupOpt.isPresent()) {
                logger.warn("恢复失败 - 备份不存在: {}", backupId);
                return false;
            }

            Backup backup = backupOpt.get();

            // 获取原文件信息
            Optional<FileInfo> fileOpt = fileRepository.findById(backup.getFileId());
            if (!fileOpt.isPresent()) {
                logger.warn("恢复失败 - 原文件记录不存在: {}", backup.getFileId());
                return false;
            }

            FileInfo fileInfo = fileOpt.get();

            // 检查备份文件是否存在
            Path backupPath = Paths.get(backup.getBackupPath());
            if (!Files.exists(backupPath)) {
                logger.warn("恢复失败 - 备份文件不存在: {}", backup.getBackupPath());
                return false;
            }

            // 验证备份文件完整性
            FileIntegrityService.FileIntegrityInfo result = fileIntegrityService.calculateFileIntegrity(backup.getBackupPath(), FileIntegrityService.HashAlgorithm.MD5);
            if(result.isSuccess())
            {
                String currentHash = result.getHash(FileIntegrityService.HashAlgorithm.MD5);
                if (!backup.getBackupFileHash().equals(currentHash)) {
                    logger.warn("恢复失败 - 备份文件已损坏: backupId={}", backupId);
                    return false;
                }
            }
            else
            {
                logger.warn("恢复失败 - 备份文件已损坏: backupId={}", backupId);
                return false;
            }

            // 恢复文件
            Path originalPath = Paths.get(fileInfo.getFilePath());
            createDirectoriesIfNotExists(originalPath.getParent());
            Files.copy(backupPath, originalPath, StandardCopyOption.REPLACE_EXISTING);

            // 更新文件状态
            if (fileInfo.getIsDeleted()) {
                fileInfo.restore();
                fileRepository.save(fileInfo);
            }

            logger.info("文件恢复成功: fileId={}, backupId={}", backup.getFileId(), backupId);
            return true;

        } catch (Exception e) {
            logger.error("恢复文件时发生错误: backupId={}", backupId, e);
            return false;
        }
    }

    /**
     * 获取文件的备份列表
     * @param fileId 文件ID
     * @return 备份列表
     */
    public List<Backup> getFileBackups(Long fileId) {
        return backupRepository.findByFileId(fileId);
    }

    /**
     * 删除备份
     * @param backupId 备份ID
     * @return 删除结果
     */
    @Transactional
    public boolean deleteBackup(Long backupId) {
        try {
            Optional<Backup> backupOpt = backupRepository.findById(backupId);
            if (!backupOpt.isPresent()) {
                logger.warn("删除备份失败 - 备份不存在: {}", backupId);
                return false;
            }

            Backup backup = backupOpt.get();

            // 删除备份文件
            try {
                Path backupPath = Paths.get(backup.getBackupPath());
                Files.deleteIfExists(backupPath);
                logger.info("备份文件已删除: {}", backup.getBackupPath());
            } catch (IOException e) {
                logger.warn("删除备份文件失败: {}", e.getMessage());
            }

            // 删除备份记录
            backupRepository.delete(backup);

            logger.info("备份已删除: backupId={}", backupId);
            return true;

        } catch (Exception e) {
            logger.error("删除备份时发生错误: backupId={}", backupId, e);
            return false;
        }
    }

    /**
     * 定时清理过期备份
     */
    @Scheduled(cron = "${clouddms.backup.cleanup-schedule:0 0 2 * * ?}") // 每天凌晨2点执行
    public void cleanupExpiredBackups() {
        if (!backupEnabled) {
            return;
        }

        try {
            logger.info("开始清理过期备份，保留天数: {}", retentionDays);

            LocalDateTime expiredBefore = LocalDateTime.now().minusDays(retentionDays);
            List<Backup> expiredBackups = backupRepository.findExpiredBackups(expiredBefore);

            int deletedCount = 0;
            for (Backup backup : expiredBackups) {
                if (deleteBackup(backup.getBackupId())) {
                    deletedCount++;
                }
            }

            logger.info("过期备份清理完成，删除数量: {}", deletedCount);

        } catch (Exception e) {
            logger.error("清理过期备份时发生错误", e);
        }
    }

    /**
     * 获取备份统计信息
     * @return 备份统计信息
     */
    public BackupStats getBackupStats() {
        try {
            long totalBackups = backupRepository.count();
            Long totalSize = backupRepository.sumTotalBackupSize();
            long autoBackups = backupRepository.countByBackupType(Backup.BackupType.AUTO);
            long manualBackups = backupRepository.countByBackupType(Backup.BackupType.MANUAL);

            return new BackupStats(
                totalBackups,
                totalSize != null ? totalSize : 0L,
                autoBackups,
                manualBackups
            );
        } catch (Exception e) {
            logger.error("获取备份统计信息失败", e);
            return new BackupStats(0, 0L, 0, 0);
        }
    }

    /**
     * 生成备份路径
     */
    private Path generateBackupPath(FileInfo fileInfo) {
        return Paths.get(backupBasePath, 
                "user_" + fileInfo.getUserId(), 
                fileInfo.getStoredFilename());
    }

    /**
     * 创建目录（如果不存在）
     */
    private void createDirectoriesIfNotExists(Path dirPath) throws IOException {
        if (!Files.exists(dirPath)) {
            Files.createDirectories(dirPath);
        }
    }

    // IService 接口实现
    @Override
    public String getServiceName() {
        return "backup-service";
    }

    @Override
    public String getDisplayName() {
        return "备份服务";
    }

    @Override
    public String getDescription() {
        return "提供文件自动备份、手动备份和备份管理功能";
    }

    @Override
    public ServiceStatus getStatus() {
        if (!backupEnabled) {
            return ServiceStatus.DISABLED;
        }
        
        try {
            // 检查数据库连接
            backupRepository.count();
            // 检查备份路径
            Path backupPath = Paths.get(backupBasePath);
            if (!Files.exists(backupPath)) {
                Files.createDirectories(backupPath);
            }
            return ServiceStatus.RUNNING;
        } catch (Exception e) {
            logger.error("备份服务状态检查失败: {}", e.getMessage());
            return ServiceStatus.DISABLED;
        }
    }

    @Override
    public HealthStatus getHealthStatus() {
        try {
            if (!backupEnabled) {
                return new HealthStatus(true, "备份服务已禁用");
            }

            long backupCount = backupRepository.count();
            Path backupPath = Paths.get(backupBasePath);
            boolean storageAccessible = Files.exists(backupPath) && Files.isWritable(backupPath);
            
            if (storageAccessible) {
                return new HealthStatus(true, "服务正常运行，备份总数: " + backupCount);
            } else {
                return new HealthStatus(false, "备份存储路径不可访问: " + backupPath);
            }
        } catch (Exception e) {
            return new HealthStatus(false, "健康检查失败: " + e.getMessage());
        }
    }

    @Override
    public int getPriority() {
        return 3; // 中等优先级
    }

    @Override
    public String getVersion() {
        return "1.0.0";
    }

    /**
     * 获取备份配置信息（管理员功能）
     * @return 备份配置信息
     */
    public Map<String, Object> getBackupConfig() {
        Map<String, Object> config = new HashMap<>();
        config.put("enabled", backupEnabled);
        config.put("autoBackupEnabled", autoBackupEnabled);
        config.put("backupBasePath", backupBasePath);
        config.put("maxBackupSize", maxBackupSize);
        config.put("retentionDays", retentionDays);
        config.put("autoBackupInterval", autoBackupInterval);
        return config;
    }

    /**
     * 更新备份配置信息（管理员功能）
     * @param config 新的配置信息
     * @return 更新是否成功
     */
    public boolean updateBackupConfig(Map<String, Object> config) {
        try {
            logger.info("管理员更新备份配置: {}", config);
            
            // 注意：这里只是示例实现，实际生产环境中应该将配置持久化到数据库或配置文件
            // 当前实现仅在运行时有效，重启后会恢复到配置文件的值
            
            if (config.containsKey("enabled")) {
                this.backupEnabled = (Boolean) config.get("enabled");
            }
            if (config.containsKey("autoBackupEnabled")) {
                this.autoBackupEnabled = (Boolean) config.get("autoBackupEnabled");
            }
            if (config.containsKey("maxBackupSize")) {
                this.maxBackupSize = Long.parseLong(config.get("maxBackupSize").toString());
            }
            if (config.containsKey("retentionDays")) {
                this.retentionDays = Integer.parseInt(config.get("retentionDays").toString());
            }
            if (config.containsKey("autoBackupInterval")) {
                this.autoBackupInterval = Integer.parseInt(config.get("autoBackupInterval").toString());
            }
            
            logger.info("备份配置更新成功");
            return true;
            
        } catch (Exception e) {
            logger.error("更新备份配置失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取系统中所有备份（管理员功能）
     * @return 所有备份列表
     */
    public List<Backup> getAllBackups() {
        return backupRepository.findAll();
    }

    /**
     * 备份结果类
     */
    public static class BackupResult {
        private final boolean success;
        private final String message;
        private final Backup backup;

        private BackupResult(boolean success, String message, Backup backup) {
            this.success = success;
            this.message = message;
            this.backup = backup;
        }

        public static BackupResult success(Backup backup, String message) {
            return new BackupResult(true, message, backup);
        }

        public static BackupResult failure(String message) {
            return new BackupResult(false, message, null);
        }

        public boolean isSuccess() { return success; }
        public String getMessage() { return message; }
        public Backup getBackup() { return backup; }
    }

    /**
     * 备份统计信息类
     */
    public static class BackupStats {
        private final long totalBackups;
        private final long totalSize;
        private final long autoBackups;
        private final long manualBackups;

        public BackupStats(long totalBackups, long totalSize, long autoBackups, long manualBackups) {
            this.totalBackups = totalBackups;
            this.totalSize = totalSize;
            this.autoBackups = autoBackups;
            this.manualBackups = manualBackups;
        }

        public long getTotalBackups() { return totalBackups; }
        public long getTotalSize() { return totalSize; }
        public long getAutoBackups() { return autoBackups; }
        public long getManualBackups() { return manualBackups; }
    }
}